#ifndef NE_STL_INCLUDENE_STL_TUPLE_H
#define NE_STL_INCLUDENE_STL_TUPLE_H

#include "ne_stl_any.h"
#include "ne_stl_build.h"
#include "ne_stl_continuous_container.h"
#include "ne_stl_memory.h"

namespace nstd {

template <int32_t... Ns>
struct int_sequence {};
template <int32_t... Ns>
struct int_sequence_gen;
template <int32_t I, int32_t... Ns>
struct int_sequence_gen<I, Ns...> {
    using type = typename int_sequence_gen<I - 1, I - 1, Ns...>::type;
};
template <int32_t... Ns>
struct int_sequence_gen<0, Ns...> {
    using type = int_sequence<Ns...>;
};
template <int32_t N>
using sequence_t = typename int_sequence_gen<N>::type;
template <typename... TArgs>
struct tuple;
template <>
struct tuple<> {
    std::size_t size() const { return values_.size(); }
    nstd::vector<nstd::any> values_;
};
template <typename T, typename... TArgs>
struct tuple<T, TArgs...> : tuple<TArgs...> {
    using value_type = T;
    using base_type = tuple<TArgs...>;
    using this_type = tuple<T, TArgs...>;
    using base_type::values_;
    tuple(const T& v, const TArgs&... tails)
        : tuple<TArgs...>(tails...) {
        values_.push_back(v);
    }
};
template <uint32_t N, typename... TArgs>
struct tuple_at;
template <uint32_t N, typename T, typename... TArgs>
struct tuple_at<N, tuple<T, TArgs...> > {
    using value_type = typename tuple_at<N - 1, tuple<TArgs...> >::value_type;
    using tuple_type = typename tuple_at<N - 1, tuple<TArgs...> >::tuple_type;
};
template <typename T, typename... TArgs>
struct tuple_at<0, tuple<T, TArgs...> > {
    using value_type = T;
    using tuple_type = tuple<T, TArgs...>;
};
template <uint32_t N, typename... TArgs>
const typename tuple_at<N, tuple<TArgs...> >::value_type& tuple_get(const tuple<TArgs...>& tuple_) {
    return nstd::any_cast<const typename tuple_at<N, tuple<TArgs...> >::value_type&>(tuple_.values_[tuple_.values_.size() - 1 - N]);
}
template <uint32_t N, typename... TArgs>
typename tuple_at<N, tuple<TArgs...> >::value_type tuple_get(tuple<TArgs...>& tuple_) {
    using tuple_type = tuple<TArgs...>;
    using base_tuple_type = typename tuple_at<N, tuple_type>::tuple_type;
    return nstd::any_cast<typename tuple_at<N, tuple<TArgs...> >::value_type&>(*(tuple_.values_.rbegin() + N));
}
template <typename... Type>
tuple<Type...> make_tuple(const Type&... args) {
    return tuple<Type...>(args...);
}
template <typename TTuple, uint32_t... Ns>
auto extract_tuple(TTuple& tup) -> decltype(make_tuple(tuple_get<Ns>(tup)...)) {
    return make_tuple(tuple_get<Ns>(tup)...);
}
template <uint32_t... Ns, typename... TArgs>
auto extract_tuple(tuple<TArgs...>& tup) -> decltype(make_tuple(tuple_get<Ns>(tup)...)) {
    return make_tuple(tuple_get<Ns>(tup)...);
}
}  // namespace nstd

#endif  // !NE_STL_INCLUDENE_STL_TUPLE_H
